package com.gitee.wsl.android.ext.store

import android.content.ContentUris
import android.database.Cursor
import android.net.Uri
import android.os.Environment
import android.provider.DocumentsContract
import android.provider.MediaStore
import androidx.core.net.toUri
import com.gitee.wsl.android.ext.ApplicationInit.application
import com.gitee.wsl.android.ext.store.PathExt.External.externalCacheDirPath
import com.gitee.wsl.android.ext.store.PathExt.Internal.internalCacheDirPath
import com.gitee.wsl.jvm.ext.store.fileSeparator
import java.io.File
import java.util.regex.Pattern


object PathExt{

    const val EXTERNAL_STORAGE_AUTHORITY = "com.android.externalstorage.documents"

    /*
    File picker for each API version gives the following URIs:
    * API 26 - 27 => content://com.android.providers.downloads.documents/document/22
    * API 28 - 29 => content://com.android.providers.downloads.documents/document/raw%3A%2Fstorage%2Femulated%2F0%2FDownload%2Fscreenshot.jpeg
    * API 30+     => content://com.android.providers.downloads.documents/document/msf%3A42
     */
    const val DOWNLOADS_FOLDER_AUTHORITY = "com.android.providers.downloads.documents"

    const val MEDIA_FOLDER_AUTHORITY = "com.android.providers.media.documents"

    /**
     * Only available on API 26 to 29.
     */
    const val DOWNLOADS_TREE_URI = "content://$DOWNLOADS_FOLDER_AUTHORITY/tree/downloads"

    /**
     * Only available on API 24 to 29.
     */
    const val DOCUMENTS_TREE_URI = "content://$EXTERNAL_STORAGE_AUTHORITY/tree/home%3A"


    val FILE_NAME_DUPLICATION_REGEX_WITH_EXTENSION = Regex("(.*?) \\(\\d+\\)\\.[a-zA-Z0-9]+")


    val FILE_NAME_DUPLICATION_REGEX_WITHOUT_EXTENSION = Regex("(.*?) \\(\\d+\\)")


    val SD_CARD_STORAGE_ID_REGEX = Regex("[A-Z0-9]{4}-[A-Z0-9]{4}")


    val SD_CARD_STORAGE_PATH_REGEX = Regex("/storage/$SD_CARD_STORAGE_ID_REGEX(.*?)")

    const val KITKAT_SD_CARD_ID = "sdcard"

    const val KITKAT_SD_CARD_PATH = "/storage/$KITKAT_SD_CARD_ID"

    fun getKitkatSdCardRootFile(basePath: String = "") = File(KITKAT_SD_CARD_PATH + "/$basePath".trimEnd('/'))

    @JvmStatic
    fun isRootUri(uri: Uri): Boolean {
        val path = uri.path ?: return false
        return uri.isExternalStorageDocument && path.indexOf(':') == path.length - 1
                && !path.startsWith("/tree/home:") // Do not treat Documents folder as root
    }

     fun randomFilePathUri(dirLocation:String = cacheDirPath, suffixes:String="txt"): Uri =
         randomFilePath(dirLocation, suffixes).toUri()

     fun filePath(dirLocation:String = cacheDirPath, filename:String, suffixes:String="txt"): Uri =
         File("$dirLocation$fileSeparator$filename.$suffixes").toUri()

    fun randomFilePath(dirLocation:String = cacheDirPath, suffixes:String="txt"): File =
        File("$dirLocation$fileSeparator${System.currentTimeMillis()}.$suffixes")

     fun cacheFilePath(filename:String,suffixes:String): File =
         File("$cacheDirPath$fileSeparator$filename.$suffixes")

    fun cacheFilePath(filename:String): File =
        File("$cacheDirPath$fileSeparator$filename")

    /**
     * Get a file path from a Uri. This will get the the path for Storage Access
     * Framework Documents, as well as the _data field for the MediaStore and
     * other file-based ContentProviders.
     *
     * @param uri     The Uri to query.
     */
    fun getPath(uri: Uri): String? {
        val isKitKat = true

        // DocumentProvider
        if (isKitKat && DocumentsContract.isDocumentUri(application, uri)) {
            // ExternalStorageProvider
            if (isExternalStorageDocument(uri)) {
                val docId = DocumentsContract.getDocumentId(uri)
                val split = docId.split(":".toRegex()).dropLastWhile { it.isEmpty() }
                    .toTypedArray()
                val type = split[0]
                if ("primary".equals(type, ignoreCase = true)) {
                    return Environment.getExternalStorageDirectory().toString() + "/" + split[1]
                }

                // TODO handle non-primary volumes
            } else if (isDownloadsDocument(uri)) {
                val id = DocumentsContract.getDocumentId(uri)
                val contentUri = ContentUris.withAppendedId(
                    Uri.parse("content://downloads/public_downloads"), id.toLong()
                )
                return getDataColumn(contentUri, null, null)
            } else if (isMediaDocument(uri)) {
                val docId = DocumentsContract.getDocumentId(uri)
                val split = docId.split(":".toRegex()).dropLastWhile { it.isEmpty() }
                    .toTypedArray()
                val type = split[0]
                var contentUri: Uri? = null
                if ("image" == type) {
                    contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
                } else if ("video" == type) {
                    contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI
                } else if ("audio" == type) {
                    contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
                }
                val selection = "_id=?"
                val selectionArgs = arrayOf(
                    split[1]
                )
                return getDataColumn(contentUri!!, selection, selectionArgs)
            }
        } else if ("content".equals(uri.scheme, ignoreCase = true)) {
            return getDataColumn(uri, null, null)
        } else if ("file".equals(uri.scheme, ignoreCase = true)) {
            return uri.path
        }
        return null
    }

    /**
     * Get the value of the data column for this Uri. This is useful for
     * MediaStore Uris, and other file-based ContentProviders.
     *
     * @param uri           The Uri to query.
     * @param selection     (Optional) Filter used in the query.
     * @param selectionArgs (Optional) Selection arguments used in the query.
     * @return The value of the _data column, which is typically a file path.
     */
    fun getDataColumn(
        uri: Uri, selection: String?,
        selectionArgs: Array<String>?
    ): String? {
        var cursor: Cursor? = null
        val column = "_data"
        val projection = arrayOf(
            column
        )
        try {
            cursor = application.contentResolver.query(
                uri, projection, selection, selectionArgs,
                null
            )
            if (cursor != null && cursor.moveToFirst()) {
                val column_index = cursor.getColumnIndexOrThrow(column)
                return cursor.getString(column_index)
            }
        } finally {
            cursor?.close()
        }
        return null
    }


    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is ExternalStorageProvider.
     */
    fun isExternalStorageDocument(uri: Uri): Boolean {
        return "com.android.externalstorage.documents" == uri.authority
    }

    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is DownloadsProvider.
     */
    fun isDownloadsDocument(uri: Uri): Boolean {
        return "com.android.providers.downloads.documents" == uri.authority
    }

    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is MediaProvider.
     */
    fun isMediaDocument(uri: Uri): Boolean {
        return "com.android.providers.media.documents" == uri.authority
    }


    object Internal{
        inline val internalCacheDirPath: String
            get() = application.cacheDir.absolutePath

        inline val internalFileDirPath: String
            get() = application.filesDir.absolutePath

        inline val internalPicturesDirPath: String?
            get() = application.getFileStreamPath(Environment.DIRECTORY_PICTURES)?.absolutePath

        inline val internalMoviesDirPath: String?
            get() = application.getFileStreamPath(Environment.DIRECTORY_MOVIES)?.absolutePath

        inline val internalDownloadsDirPath: String?
            get() = application.getFileStreamPath(Environment.DIRECTORY_DOWNLOADS)?.absolutePath

        inline val internalDocumentsDirPath: String?
            get() = application.getFileStreamPath(Environment.DIRECTORY_DOCUMENTS)?.absolutePath

        inline val internalMusicDirPath: String?
            get() = application.getFileStreamPath(Environment.DIRECTORY_MUSIC)?.absolutePath

        inline val internalPodcastsDirPath: String?
            get() = application.getFileStreamPath(Environment.DIRECTORY_PODCASTS)?.absolutePath

        inline val internalRingtonesDirPath: String?
            get() = application.getFileStreamPath(Environment.DIRECTORY_RINGTONES)?.absolutePath

        inline val internalAlarmsDirPath: String?
            get() = application.getFileStreamPath(Environment.DIRECTORY_ALARMS)?.absolutePath

        inline val internalNotificationsDirPath: String?
            get() = application.getFileStreamPath(Environment.DIRECTORY_NOTIFICATIONS)?.absolutePath
    }

    object External{
        inline val externalCacheDirPath: String?
            get() = application.externalCacheDir?.absolutePath

        inline val externalFilesDirPath: String?
            get() = application.getExternalFilesDir(null)?.absolutePath


        inline val externalPicturesDirPath: String?
            get() = application.getExternalFilesDir(Environment.DIRECTORY_PICTURES)?.absolutePath

        inline val externalMoviesDirPath: String?
            get() = application.getExternalFilesDir(Environment.DIRECTORY_MOVIES)?.absolutePath

        inline val externalDownloadsDirPath: String?
            get() = application.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)?.absolutePath

        inline val externalDocumentsDirPath: String?
            get() = application.getFileStreamPath(Environment.DIRECTORY_DOCUMENTS)?.absolutePath

        inline val externalMusicDirPath: String?
            get() = application.getExternalFilesDir(Environment.DIRECTORY_MUSIC)?.absolutePath

        inline val externalPodcastsDirPath: String?
            get() = application.getExternalFilesDir(Environment.DIRECTORY_PODCASTS)?.absolutePath

        inline val externalRingtonesDirPath: String?
            get() = application.getExternalFilesDir(Environment.DIRECTORY_RINGTONES)?.absolutePath

        inline val externalAlarmsDirPath: String?
            get() = application.getExternalFilesDir(Environment.DIRECTORY_ALARMS)?.absolutePath

        inline val externalNotificationsDirPath: String?
            get() = application.getExternalFilesDir(Environment.DIRECTORY_NOTIFICATIONS)?.absolutePath

    }
}

const val assetDirPath:String="/android_asset/"

inline val cacheDirPath: String
    get() = if (isExternalStorageWritable || !isExternalStorageRemovable)
        externalCacheDirPath.orEmpty()
    else
        internalCacheDirPath

/**
 * Checks if a volume containing external storage is available for read and write.
 */
inline val isExternalStorageWritable: Boolean
    get() = Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED

/**
 * Checks if a volume containing external storage is available to at least read.
 */
inline val isExternalStorageReadable: Boolean
    get() = Environment.getExternalStorageState() in setOf(
        Environment.MEDIA_MOUNTED,
        Environment.MEDIA_MOUNTED_READ_ONLY
    )

inline val isExternalStorageRemovable: Boolean
    get() = Environment.isExternalStorageRemovable()

fun String.toSdcardPath(): String {
    return Environment.getExternalStorageDirectory().absolutePath.toString() + "/" + this
}

fun String.getFileNameFromUrl(): String? {
    var temp = this
    if (temp.isNotEmpty()) {
        val fragment = temp.lastIndexOf('#')
        if (fragment > 0) {
            temp = temp.substring(0, fragment)
        }

        val query = temp.lastIndexOf('?')
        if (query > 0) {
            temp = temp.substring(0, query)
        }

        val filenamePos = temp.lastIndexOf('/')
        val filename = if (0 <= filenamePos) temp.substring(filenamePos + 1) else temp

        if (filename.isNotEmpty() && Pattern.matches("[a-zA-Z_0-9.\\-()%]+", filename)) {
            return filename
        }
    }
    return null
}